// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"

#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/buildflag.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/signin/profile_colors_util.h"
#include "chrome/browser/ui/webui/signin/sync_confirmation_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/themes/autogenerated_theme_util.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/signin_resources.h"
#include "components/signin/public/base/avatar_icon_util.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/resource_path.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/color_utils.h"
#include "ui/native_theme/native_theme.h"
#include "ui/resources/grit/webui_generated_resources.h"
#include "ui/resources/grit/webui_resources.h"

namespace {

ProfileThemeColors GetProfileThemeColors(absl::optional<SkColor> theme_color,
                                         bool dark_mode) {
  return theme_color.has_value()
             ? GetProfileThemeColorsForAutogeneratedColor(*theme_color)
             : GetDefaultProfileThemeColors(dark_mode);
}

}  // namespace

SyncConfirmationUI::SyncConfirmationUI(content::WebUI* web_ui)
    : SigninWebDialogUI(web_ui), profile_(Profile::FromWebUI(web_ui)) {
  // Initializing the WebUIDataSource in the constructor is needed for polymer
  // tests.
  Initialize(/*profile_creation_flow_color=*/absl::nullopt,
             DesignVersion::kMonotone,
             /*is_modal_dialog=*/true);
}

SyncConfirmationUI::~SyncConfirmationUI() = default;

void SyncConfirmationUI::InitializeMessageHandlerWithBrowser(Browser* browser) {
  InitializeMessageHandler(browser);
}

void SyncConfirmationUI::InitializeMessageHandlerForCreationFlow(
    absl::optional<SkColor> profile_color) {
  // Redo the initialization with `profile_color`.
  Initialize(profile_color, DesignVersion::kColored, /*is_modal_dialog=*/false);
  InitializeMessageHandler(/*browser=*/nullptr);
}

void SyncConfirmationUI::Initialize(
    absl::optional<SkColor> profile_creation_flow_color,
    DesignVersion design,
    bool is_modal_dialog) {
  DCHECK(!(design == DesignVersion::kMonotone &&
           profile_creation_flow_color.has_value()));
  const bool is_sync_allowed = SyncServiceFactory::IsSyncAllowed(profile_);

  content::WebUIDataSource* source =
      content::WebUIDataSource::Create(chrome::kChromeUISyncConfirmationHost);
  webui::SetJSModuleDefaults(source);

  static constexpr webui::ResourcePath kResources[] = {
      {"signin_shared_css.js", IDR_SIGNIN_SIGNIN_SHARED_CSS_JS},
      {"signin_vars_css.js", IDR_SIGNIN_SIGNIN_VARS_CSS_JS},
      {"sync_confirmation_browser_proxy.js",
       IDR_SIGNIN_SYNC_CONFIRMATION_SYNC_CONFIRMATION_BROWSER_PROXY_JS},
      {"sync_confirmation.js",
       IDR_SIGNIN_SYNC_CONFIRMATION_SYNC_CONFIRMATION_JS},
      {chrome::kChromeUISyncConfirmationLoadingPath,
       IDR_SIGNIN_SYNC_CONFIRMATION_SYNC_LOADING_CONFIRMATION_HTML},
  };
  source->AddResourcePaths(kResources);

  AddStringResource(source, "syncLoadingConfirmationTitle",
                    IDS_SYNC_LOADING_CONFIRMATION_TITLE);

  if (is_sync_allowed) {
    InitializeForSyncConfirmation(source, profile_creation_flow_color, design,
                                  is_modal_dialog);
  } else {
    InitializeForSyncDisabled(source);
  }

  base::DictionaryValue strings;
  webui::SetLoadTimeDataDefaults(
      g_browser_process->GetApplicationLocale(), &strings);
  source->AddLocalizedStrings(strings);

  content::WebUIDataSource::Add(profile_, source);
}

void SyncConfirmationUI::InitializeMessageHandler(Browser* browser) {
  web_ui()->AddMessageHandler(std::make_unique<SyncConfirmationHandler>(
      profile_, js_localized_string_to_ids_map_, browser));
}

void SyncConfirmationUI::InitializeForSyncConfirmation(
    content::WebUIDataSource* source,
    absl::optional<SkColor> profile_creation_flow_color,
    DesignVersion design,
    bool is_modal_dialog) {
  AddStringResource(source, "syncConfirmationTitle",
#if BUILDFLAG(IS_CHROMEOS_LACROS)
                    IDS_SYNC_CONFIRMATION_TITLE_LACROS
#else
                    IDS_SYNC_CONFIRMATION_TITLE
#endif
  );
  AddStringResource(source, "syncConfirmationSyncInfoTitle",
#if BUILDFLAG(IS_CHROMEOS_LACROS)
                    IDS_SYNC_CONFIRMATION_SYNC_INFO_TITLE_LACROS
#else
                    IDS_SYNC_CONFIRMATION_SYNC_INFO_TITLE
#endif
  );
  AddStringResource(source, "syncConfirmationConfirmLabel",
#if BUILDFLAG(IS_CHROMEOS_LACROS)
                    IDS_DONE
#else
                    IDS_SYNC_CONFIRMATION_CONFIRM_BUTTON_LABEL
#endif
  );
  AddStringResource(source, "syncConfirmationSyncInfoDesc",
                    IDS_SYNC_CONFIRMATION_SYNC_INFO_DESC);
  AddStringResource(source, "syncConfirmationSettingsInfo",
                    IDS_SYNC_CONFIRMATION_SETTINGS_INFO);

  source->AddResourcePath(
      "sync_confirmation_app.js",
      IDR_SIGNIN_SYNC_CONFIRMATION_SYNC_CONFIRMATION_APP_JS);
  source->SetDefaultResource(
      IDR_SIGNIN_SYNC_CONFIRMATION_SYNC_CONFIRMATION_HTML);

  source->AddBoolean("isNewDesign", design == DesignVersion::kColored);

  source->AddBoolean("isModalDialog", is_modal_dialog);

  switch (design) {
    case DesignVersion::kMonotone: {
      source->AddString("accountPictureUrl",
                        profiles::GetPlaceholderAvatarIconUrl());
      AddStringResource(source, "syncConfirmationUndoLabel", IDS_CANCEL);
      AddStringResource(source, "syncConfirmationSettingsLabel",
                        IDS_SYNC_CONFIRMATION_SETTINGS_BUTTON_LABEL);

      source->AddResourcePath(
          "images/sync_confirmation_illustration.svg",
          IDR_SIGNIN_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_ILLUSTRATION_SVG);
      source->AddResourcePath(
          "images/sync_confirmation_illustration_dark.svg",
          IDR_SIGNIN_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_ILLUSTRATION_DARK_SVG);
      break;
    }
    case DesignVersion::kColored: {
      bool dark_mode = webui::GetNativeTheme(web_ui()->GetWebContents())
                           ->ShouldUseDarkColors();
      ProfileThemeColors colors =
          GetProfileThemeColors(profile_creation_flow_color, dark_mode);
      const int kAccountPictureSize = 128;
      gfx::Image avatar_picture = profiles::GetPlaceholderAvatarIconWithColors(
          colors.default_avatar_fill_color, colors.default_avatar_stroke_color,
          kAccountPictureSize);
      std::string avatar_picture_url =
          webui::GetBitmapDataUrl(avatar_picture.AsBitmap());
      source->AddString("accountPictureUrl", avatar_picture_url);

      AddStringResource(source, "syncConfirmationUndoLabel", IDS_NO_THANKS);
      AddStringResource(source, "syncConfirmationSettingsLabel",
                        IDS_SYNC_CONFIRMATION_REFRESHED_SETTINGS_BUTTON_LABEL);
      source->AddString("highlightColor", color_utils::SkColorToRgbaString(
                                              colors.profile_highlight_color));

      source->AddResourcePath(
          "images/sync_confirmation_refreshed_illustration.svg",
          IDR_SIGNIN_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_REFRESHED_ILLUSTRATION_SVG);
      source->AddResourcePath(
          "images/sync_confirmation_refreshed_illustration_dark.svg",
          IDR_SIGNIN_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_REFRESHED_ILLUSTRATION_DARK_SVG);
      break;
    }
  }
}

void SyncConfirmationUI::InitializeForSyncDisabled(
    content::WebUIDataSource* source) {
  source->SetDefaultResource(
      IDR_SIGNIN_SYNC_CONFIRMATION_SYNC_DISABLED_CONFIRMATION_HTML);
  source->AddResourcePath(
      "sync_disabled_confirmation_app.js",
      IDR_SIGNIN_SYNC_CONFIRMATION_SYNC_DISABLED_CONFIRMATION_APP_JS);

  AddStringResource(source, "syncDisabledConfirmationTitle",
                    IDS_SYNC_DISABLED_CONFIRMATION_CHROME_SYNC_TITLE);
  AddStringResource(source, "syncDisabledConfirmationDetails",
                    IDS_SYNC_DISABLED_CONFIRMATION_DETAILS);
  AddStringResource(source, "syncDisabledConfirmationConfirmLabel",
                    IDS_SYNC_DISABLED_CONFIRMATION_CONFIRM_BUTTON_LABEL);
  AddStringResource(source, "syncDisabledConfirmationUndoLabel",
                    IDS_SYNC_DISABLED_CONFIRMATION_UNDO_BUTTON_LABEL);
}

void SyncConfirmationUI::AddStringResource(content::WebUIDataSource* source,
                                           const std::string& name,
                                           int ids) {
  source->AddLocalizedString(name, ids);

  // When the strings are passed to the HTML, the Unicode NBSP symbol (\u00A0)
  // will be automatically replaced with "&nbsp;". This change must be mirrored
  // in the string-to-ids map. Note that "\u00A0" is actually two characters,
  // so we must use base::ReplaceSubstrings* rather than base::ReplaceChars.
  // TODO(msramek): Find a more elegant solution.
  std::string sanitized_string =
      base::UTF16ToUTF8(l10n_util::GetStringUTF16(ids));
  base::ReplaceSubstringsAfterOffset(&sanitized_string, 0, "\u00A0" /* NBSP */,
                                     "&nbsp;");

  js_localized_string_to_ids_map_[sanitized_string] = ids;
}
